[小ネタ]pytestのmark.parametrizeでサブテストに簡単に名前をつける方法
はじめに
おはようございます、加藤です。pytestのmark.parametrizeでサブテストに簡単に名前をつける方法をご紹介します。
説明
説明様に、引数同士を足すだけの簡単な関数を用意します。
def sum(x, y): return x + y
テストコードは下記の様に書きます。
import pytest from src.main import sum params = { "normal 1": (1, 2, 3), "normal 2": (3, 4, 7) } @pytest.mark.parametrize("x, y, want", list(params.values()), ids=list(params.keys())) def test(x, y, want): assert want == sum(x, y)
テストパラメータは、辞書型で定義します。Keyにテスト名を、Valueにタプル型で入力と期待する出力を定義します。
"normal 1": (1, 2, 3)
の場合は、 "normal 1"
がテスト名で、 1, 2
が入力、 3
が期待する出力です。
params = { "normal 1": (1, 2, 3), "normal 2": (3, 4, 7) }
@pytest.mark.parametrize
の第一引数で、入力及び出力に名前を付けます。先程テストパラメータで定義したタプル型の順序どおりにします。
list(<dict>.keys())
, list(<dict>.values())
で、辞書のKeyのみ、Valueのみをリスト型で取得できます。第二引数に list(<dict>.values())
を、引数ids
に list(<dict>.keys())
を定義する事で、テストパラメータの名前と値を適合させます。
@pytest.mark.parametrize("x, y, want", list(params.values()), ids=list(params.keys()))
例
参考例を1つ紹介します。
API Gateway → Lambda 構成のAPIを想定しています。テストパラメータの入力は pathParameters
で、出力は statusCode
と Body
です。
紹介した内容を使うと、こんな感じでテストが書けます。
英語がだいぶ変だと思いますが、絶賛勉強中なので、許してください。
import pytest import json // 色々インポート @pytest.mark.usefixtures('start_moto_mock', 'create_gg_group', 'create_lambda_function', 'create_rdb_records_for_test', 'create_enabled_failure_notification_email_records') class TestHogeHoge: params_for_res_ok = { 'have child': ( { 'pathParameters': { 'id': '1', }, }, 200, json.dumps( { 'id': 1, 'name': 'TEST NAME', 'chlid': { 'id': 2, 'name': 'CHILD NAME', } } ) ), 'don\'t have child': ( { 'pathParameters': { 'id': '1', }, }, 200, json.dumps( { 'id': 1, 'name': 'TEST NAME', 'chlid': {} } ) ) } params_for_res_not_found = { 'not found': ( { 'pathParameters': { 'id': '9999', }, }, 404, json.dumps( { 'errors': [ { 'error_code': 'E404001', 'message': 'the specified object was not found' } ] } ) ), // 権限不一致によるエラーだが存在を非権限者に伝えないようにNotFoundを返す 'permission error': ( { 'pathParameters': { 'id': '8888', }, }, 404, json.dumps( { 'errors': [ { 'error_code': 'E404001', 'message': 'the specified object was not found' } ] } ) ) } @pytest.mark.parametrize('event, status_code, body', list(params_for_res_ok.values()), ids=list(params_for_res_ok.keys())) def test_res_ok(self, event, status_code, body): """ 正常系: 200 OK :param event: :param status_code: :param body: :return: """ res = handler(event, {}) assert res['statusCode'] == status_code assert res['body'] == body @pytest.mark.parametrize('event, status_code, body', list(params_for_res_not_found.values()), ids=list(params_for_res_not_found.keys())) def test_res_not_found(self, event, status_code, body): """ 異常系: 404 Not Found :param event: :param status_code: :param body: :return: """ res = handler(event, {}) assert res['statusCode'] == status_code assert res['body'] == body
あとがき
今回の例が特にわかりやすいのですが、正常系と異常系のコードが完全に一致しているので、まとめようと思えばまとめることができます。 だからといって、まとめ過ぎると扱いにくくなるので気をつけましょう!!(レビューで指摘を受けたので自戒を込めて)